#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <arpa/inet.h>
#include <assert.h>

#define   SIZE     10
#define   PORT     8787
#define   MAXLINE  1024
#define   LISTENQ  5
#define   IPADDR   "127.0.0.1"

typedef  struct server_context_st
{
		int cli_cnt;/*客户端个数*/
		int clifds[SIZE];/*客户端个数的集合*/
		fd_set allfds;/*句柄集合*/
		int maxfd;/*句柄最大值*/
}server_context_st;

static server_context_st* s_srv_ctx = NULL;
/*##################################################*/


static int create_server_proc(const char* ip,int port)
{
	int fd;
	struct sockaddr_in servaddr;
	fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd == -1)
	{
		fprintf(stderr ,"create socket fail,errno:%d,reson:%s\n",errno,strerror(errno));
		return -1;
	}

	/*一个端口释放后会等待两分钟只收才能被再次使用，SO_REUSEADDR是让端口释放后立即就可以被再次使用*/

	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET,ip,&servaddr.sin_addr);
	servaddr.sin_port = htons(port);
  
	if(bind(fd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
	{
		perror("bind error:");
		return -1;
	}

	listen(fd,LISTENQ);
	return fd;

}

static int handle_client_msg(int fd, char *buf) 
{
	   assert(buf);
		 printf("recv buf is :%s\n", buf);
		 write(fd, buf, strlen(buf) +1);
		return 0;
}

static int accept_client_proc(int srvfd)
{
	struct sockaddr_in cliaddr;
	socklen_t cliaddrlen;
	cliaddrlen = sizeof(cliaddr);
	int clifd = -1;
	printf("accpet clint proc is called.\n");
ACCEPT:
	clifd = accept(srvfd,(struct sockaddr*)&cliaddr,&cliaddrlen);

	if(clifd == -1)
	{
		if(errno == EINTR)
		{
			goto ACCEPT;
		}
		else
		{
			fprintf(stderr,"accept fail,error:%s\n",strerror(errno));
			return -1;
		}
	}

	fprintf(stdout,"accept a new client :%s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);

	//将新的连接描述符添加到数组中
	
	int i = 0;
	for(i = 0; i < SIZE;i++)
	{
		if(s_srv_ctx->clifds[i] < 0)
		{
			s_srv_ctx->clifds[i] = clifd;
			s_srv_ctx->cli_cnt++;
			break;
		}
	}

	if(i == SIZE)
	{
		fprintf(stderr,"too many clients.\n");
		return -1;
	}
}

static void recv_client_msg(fd_set *readfds)
{
	int i = 0, n = 0;
	int clifd;
	char buf[MAXLINE] = { 0 };
	for(i = 0; i <= s_srv_ctx->cli_cnt;i++)
	{
		clifd = s_srv_ctx->clifds[i];
		if(clifd < 0)
		{
			continue;
		}
		/*判断客户端套接字是否有数据*/
		if(FD_ISSET(clifd,readfds))
		{
			//接收客户端发送的消息
			n = read(clifd,buf,MAXLINE);
			if(n <= 0)
			{
				FD_CLR(clifd,&s_srv_ctx->allfds);
				close(clifd);
				s_srv_ctx->clifds[i] = -1;
				continue;
			}
			handle_client_msg(clifd,buf);
		}
	}

}

static void handle_client_proc(int srvfd)
{
	int clifd  = -1;
	int retval = 0;
  fd_set* readfds = &s_srv_ctx->allfds;
  struct timeval tv;
  int i = 0;

  while(1)
  {
		/*每次调用select前都要重新设置文件描述符和时间，因为时间发生后，文件描述符和时间都被内核修改了*/
		FD_ZERO(readfds);
		/*添加监听套接字*/
		FD_SET(srvfd,readfds);

		s_srv_ctx->maxfd = srvfd;

		tv.tv_sec = 30;
		tv.tv_usec = 0;
		/*添加客户端套接字*/

		for( i = 0; i < s_srv_ctx->cli_cnt;i++)
		{
			clifd = s_srv_ctx->clifds[i];
			/*去除无效的客户端句柄*/
			if(clifd != -1)
			{
				FD_SET(clifd,readfds);
			}
			s_srv_ctx->maxfd = (clifd > s_srv_ctx->maxfd ? clifd : s_srv_ctx->maxfd);

		}
		/*开始轮询接收处理服务端和客户端套接字*/
		retval = select(s_srv_ctx->maxfd + 1,readfds,NULL,NULL,&tv);

		if(retval == -1)
		{
			fprintf(stderr,"select error:%s.\n",strerror(errno));
			return;
		}

		if(retval == 0)
		{
			fprintf(stdout,"select is timeout.\n");
			continue;
		}

		if(FD_ISSET(srvfd,readfds))
		{
			/*监听客户端请求*/
			 accept_client_proc(srvfd);
		}
		else
		{
			/*接收处理客户端消息*/
			recv_client_msg(readfds);
		}




  }

}

static void server_uninit()
{
	if(s_srv_ctx)
	{
		free(s_srv_ctx);
		s_srv_ctx = NULL;
	}
}

static int server_init()
{
	s_srv_ctx = (server_context_st*)malloc(sizeof(server_context_st));
	if(s_srv_ctx == NULL)
	{
		return -1;
	}
	memset(s_srv_ctx,0,sizeof(server_context_st));

	int i = 0;
	for(; i < SIZE;i++)
	{
		s_srv_ctx->clifds[i] = -1;
	}
	return 0;
}

int main(int argc,char* argv[])
{
	int srvfd;
	/*初始化服务端context*/
	if (server_init() < 0)
	{
		return -1;
	}
	/*创建服务，开始监听客户端请求*/
	srvfd = create_server_proc(IPADDR,PORT);
	if (srvfd < 0)
	{
		fprintf(stderr, "socket create or bind fail.\n");
		goto err;
		/* code */
	}

	/*开始接收并处理客户端请求*/
    handle_client_proc(srvfd);
    server_uninit();

err:
	return -1;
}
